/*
 * Matthew Cramer, Jesse Martinez, Marc Mendoza 
 * CSE 325 Lab 5
 * File: QueueManager.c
 * Description: Manager that handles calls to the Queue.  This is called from
 *              the Memory manager to perform the functions to the queue.
 *
 */

#include "QueueManager.h"
#include "../MemoryManager/MemoryManager.h"
#include "../LRU/LRU.h"


/*
 * Declare functions
 */
void Enqueue(int, int, int, int, int, int, int, page_table*, int*, int*, int*, int*, memory_block*);
memory_block Dequeue(int*, int*, int*, int*, memory_block*);
memory_block Delete(int, int*, int*, int*, int*, memory_block*, memory_block*);
void iniFree(int*, int*,int, memory_block*);
void iniQueue(int*, int*, int*, int*, int*, memory_block*, int, int);

/*
 * Enqueue: Receives information from Testing Interface to put onto stack
 */
void Enqueue(int ID,int valid,int LRU,int frameType, int owner,int ptsize, int data, page_table* pagetable,
             int* head, int* tail, int* freeHead, int* freeTail,
             memory_block* queue)
{   
    
    //enqueues onto an empty queue, insert one element
    if(*head==-1 && *tail==-1)
    {
        *head = *freeHead;
		*tail = *freeHead;
		*freeHead = queue[*freeHead].nextIndex;
        if(*freeHead == -1)
        {
            *freeTail = -1;
        }
        else
        {
            queue[*freeHead].prevIndex = -1;
        }
		queue[*head].prevIndex = -1;
		queue[*tail].nextIndex = -1;
    }
	//enqueues onto a partially filled queue
	else
	{
		queue[*tail].nextIndex = *freeHead;
		queue[*freeHead].prevIndex = *tail;
		*tail = *freeHead;
		*freeHead = queue[*freeHead].nextIndex;
        //Null FreeList
        if(*freeHead == -1)
        {
            *freeTail = -1;
        }
        else
        {
            queue[*freeHead].prevIndex = -1;
        }
        queue[*tail].nextIndex = -1;
		
	}
    /*
     * Insert paramaters into struct at open slot
     */
    queue[*tail].ID = ID;
    queue[*tail].valid = valid;
    queue[*tail].LRU = LRU;
    queue[*tail].frameType = frameType;
    queue[*tail].owner = owner;
    queue[*tail].ptsize = ptsize;
    queue[*tail].data = data;
    int i;
    for(i=0; i<ptsize; i++)
    {
        queue[*tail].pagetable[i].frame = pagetable[i].frame;
        queue[*tail].pagetable[i].frameType = pagetable[i].frameType;
        queue[*tail].pagetable[i].valid = pagetable[i].valid;
    }

}

/*
 * Dequeue: returns the PID at head of Queue and Deletes it, -1 if no elements
 */
memory_block Dequeue(int* head, int* tail, int* freeHead, int* freeTail, memory_block* queue)
{
	int temp = *head;
	if( *head == *tail)//sets head and tail to null if there is only one block
	{
		*tail = -1;
		*head = -1;
	}
	else
    {
		*head = queue[*head].nextIndex;
        queue[*head].prevIndex = -1;
    }
    
    //if there is nothing on free list
	if(*freeHead == -1 && *freeTail == -1)
	{
		*freeHead = temp;
		*freeTail = temp;
		queue[*freeHead].nextIndex = -1;
		queue[*freeHead].prevIndex = -1;
	}
	else
	{
        //move free slot to free list
		queue[*freeTail].nextIndex = temp;
		queue[temp].prevIndex = *freeTail;
		queue[temp].nextIndex = -1;
		*freeTail = temp;
	}
	return queue[temp];
}

/*Delete: Receives the pid of the process to be deleted.
*		Returns the pid when delete is a successful and -2 if pid is not found.
*/
memory_block Delete(int id, int* head, int* tail, int* freeHead, int* freeTail, memory_block* queue, memory_block* temp)
{
	int i = *head; //variable to iterate through the list
	while(i != -1)
	{
		if(i == id)
		{
            //Remove the only element in the list
			if(i==*head && i==*tail)
            {
                *head = -1;
                *tail = -1;
            }
            //If PID at the head, remove head
            else if(i==*head)
			{
				*head = queue[i].nextIndex; //head is changed to the next item in the list
                if(*head == -1)
                {
                    *tail = -1;
                }
                else
                {
                    queue[*head].prevIndex = -1; //the new heads' prevIndex is reset
                }
            }
            //If PID at tail, remove tail
			else if(i==*tail)
			{
				*tail = queue[i].prevIndex; //tail is changed to the previous item in the list
				queue[*tail].nextIndex = -1; //the new tails' nextIndex is reset
			}
            //if the item is in the middle of the list
			else
			{
				//the previous is' next is set to is' next
				queue[queue[i].prevIndex].nextIndex = queue[i].nextIndex;
				//the next items' previous is set to is' previous
				queue[queue[i].nextIndex].prevIndex = queue[i].prevIndex;
			}

			//if there is nothing on free list
            if(*freeHead == -1 && *freeTail == -1)
            {
                *freeHead = i;
                *freeTail = i;
                queue[*freeHead].nextIndex = -1;
                queue[*freeHead].prevIndex = -1;
            }
            else
            {
                //move free slot to free list
                queue[*freeTail].nextIndex = i;
                queue[i].prevIndex = *freeTail;
                queue[i].nextIndex = -1;
                *freeTail = i;
            }
            
			temp[0] = queue[i];
			return temp[0];
		}
		
		i = queue[i].nextIndex;
	}
	//return error of not found
	temp[0].ID = -2;
	
	//returns no existing process error
	return temp[0];
}

//Reset the Free List for the given Queue
void iniFree(int* head, int* tail, int min, memory_block* Queue)
{
	int i;
	for(i = 0; i < *tail; i++)
	{
		Queue[i].prevIndex = i-1;
		Queue[i].nextIndex = i+1;
        Queue[i].ID = -1;
        Queue[i].valid = 0;
        Queue[i].LRU = 0;
        Queue[i].frameType = -1;
        Queue[i].owner = 0;
        
        Queue[i].data = 0;
        int j;
        for(j=0; j<Queue[i].ptsize; j++)
        {
            Queue[i].pagetable[j].frame = -1;
            Queue[i].pagetable[j].frameType = -1;
            Queue[i].pagetable[j].valid = 0;
        }
        Queue[i].ptsize = 0;
	}
	Queue[*tail].prevIndex = i-1;
	Queue[*tail].nextIndex = -1;
}

//Clears all processes in queue
void iniQueue(int* head, int* tail, int* freeHead, int* freeTail, int* size, memory_block* queue, int min, int max)
{
    *head=-1;
    *tail=-1;
    *freeHead = 0;
    *freeTail = max-1;
    *size = 0;
}

